#pragma once

#include <set>
#include <map>
#include <vector>

#include "creature.hpp"
#include "sysmap.hpp"

class Nature
{
    /**
     * @brief 这是一个有着 n * n 个坐标的虚拟世界，左上角坐标为（0，0）
     */
    int m_size = 10;

    /**
     * @brief 表示当前是第几回合
     */
    int m_round = 0;

    /**
     * @brief 系统中所有的生物集合
     */
    std::set<Creature *> m_creatures;

    /**
     * @brief 系统中所有的事件集合，在系统运行过程中按顺序发生
     */
    std::vector<Event *> m_events;

    sysmap<Creature, 10, 10> m_map;

    Nature()
    {
    }

public:
    /**
     * @brief Nature的单例模式，获取全局唯一的对象
     *
     * @return Nature*
     */
    static Nature *get_instance();

    /**
     * @brief 创建世界，初始化对应数量的动物
     *
     * @param w 狼的数量
     * @param s 羊的数量
     * @param g 草的数量
     */
    void init(int w, int s, int g);

    /**
     * @brief 运行一回合，按顺序让系统中每个生物运行一回合
     */
    void run();

    /**
     * @brief 打印当前的全局状态
     */
    void report();

    /**
     * @brief 打印当前世界
     */
    void print();

    int get_round() { return m_round; }

public:
    /**
     * @brief 获取一块随机的空地，这个坐标上还没有任何生物
     *
     * @return Position 空地的坐标
     */
    bool random_space(Position &p)
    {
        return m_map.random_space(p);
    }

    /**
     * @brief 获取指定位置上的生物
     *        TODO 应该返回一个集合，一个坐标有多个生物
     *
     * @param p 指定的位置
     * @return Creature* 如果有生物则返回其指针，没有生物则返回nullptr
     */
    std::set<Creature *> *get_creature(const Position &p)
    {
        return m_map.get_objects(p);
    }

    /**
     * @brief 在指定位置放置一个生物
     *
     * @param c
     * @param p
     */
    void put_creature(Creature *c, const Position &p)
    {
        m_map.put_object(c, p);
    }

    void remove_creature(Creature *c)
    {
        m_map.remove(c->position(), c);
    }

    /**
     * @brief 计算当前一共有多少个对象
     *
     * @return int
     */
    int total_creature()
    {
        return m_map.total();
    }

    /**
     * @brief 将数据存入文件中，主要就是将本类中的所有数据成员全部按设计的格式写入文件
     *
     * @param file 文件路径
     */
    void save(const char *file);

    /**
     * @brief 从文件中加载数据，save的反向行为
     *
     * @param file
     */
    void load(const char *file);

    bool has_position(const Position &p)
    {
        return m_map.has_position(p);
    }

    void move_to(Creature *c, const Position &p);

private:
    /**
     * @brief 将生物随机的添加到世界
     *
     * @param c 要添加的生物指针
     */
    void _random_add(Creature *c);
};